package beanstao.util.reflect;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClassUtil
{

	private static Class klass = null;

	/**
	 * 给一个接口，返回这个接口的所有实现类
	 * 
	 * @param className
	 * @param jarPath
	 * @return
	 */
	public static List<Class> getAllClassByInterfaceFromPackage(final String className, final String jarPath)
	{
		List<Class> returnClassList = new ArrayList<Class>(); // 返回结果
		// 如果不是一个接口，则不做处理

		try
		{
			List<Class> allClass = getPackageClasses(jarPath, className); // 获得当前包下以及子包下的所有类
			if (null != klass)
				// 判断是否是同一个接口
				for (Class c : allClass)
					if (klass.isAssignableFrom(c))
						if (!klass.equals(c))
							returnClassList.add(c);
		}
		catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}

		return returnClassList;
	}

	/**
	 * 给一个接口，返回这个接口的所有实现类
	 * 
	 * @param className
	 * @param jarPath
	 * @return
	 */
	public static List<Class> getAllClassByInterfaceFromJar(final String className, final String jarPath)
	{
		List<Class> returnClassList = new ArrayList<Class>(); // 返回结果
		try
		{
			List<Class> allClass = getJarClasses(jarPath, className); // 获得当前包下以及子包下的所有类
			if (null != klass)
				// 判断是否是同一个接口
				for (int i = 0; i < allClass.size(); i++)
					if (klass.isAssignableFrom(allClass.get(i)))
						if (!klass.equals(allClass.get(i)))
							returnClassList.add(allClass.get(i));
		}
		catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}

		return returnClassList;
	}

	/**
	 * 获得包中所有类，必须在类的搜索路径中
	 * 
	 * @param packageName 包名称
	 * @param className 要查找的类名称
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	private static List<Class> getPackageClasses(final String packageName, final String className) throws ClassNotFoundException, IOException
	{
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String path = packageName.replace('.', '/');
		Enumeration<URL> resources = classLoader.getResources(path);
		List<File> dirs = new ArrayList<File>();
		while (resources.hasMoreElements())
		{
			URL resource = resources.nextElement();
			dirs.add(new File(resource.getFile()));
		}
		ArrayList<Class> classes = new ArrayList<Class>();
		for (File directory : dirs)
			classes.addAll(findClasses(directory, packageName, className));
		return classes;
	}

	/**
	 * 查找包中的类 ，此时包名必须在类的搜索路径中，否则会抛出ClasseNotFoundException异常。
	 * 
	 * @param directory 包转转化后的file对象
	 * @param packageName 包名称
	 * @param className 类名称
	 * @return
	 * @throws ClassNotFoundException
	 */
	private static List<Class> findClasses(final File directory, final String packageName, final String className) throws ClassNotFoundException
	{
		List<Class> classes = new ArrayList<Class>();
		if (!directory.exists())
			return classes;
		File[] files = directory.listFiles();
		for (File file : files)
			if (file.isDirectory())
			{
				assert !file.getName().contains(".");
				classes.addAll(findClasses(file, packageName + "." + file.getName(), className));
			}
			else if (file.getName().endsWith(".class"))
			{
				Class tempObj = Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6));
				if (file.getName().equals(className + ".class"))
					klass = tempObj;
				classes.add(tempObj);
			}
		return classes;
	}

	/**
	 * 加载jar中的类文件
	 * 
	 * @param jarPath jar的路径，可以不在类的搜索路径中
	 * @param className 类名称
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	private static List<Class> getJarClasses(final String jarPath, final String className) throws ClassNotFoundException, IOException
	{
		List<Class> result = new Vector<Class>();
		JarFile jarFile = new JarFile(jarPath);
		Enumeration files = jarFile.entries();
		while (files.hasMoreElements())
		{
			JarEntry entry = (JarEntry) files.nextElement();
			String entryName = entry.getName();
			if (entryName.endsWith(".class"))
			{
				URL url = new URL("jar:file:" + jarPath + "!/" + entryName);
				new URLClassLoader(new URL[] { url });
				String classNamePackage = entryName.substring(0, entryName.lastIndexOf(".")).replaceAll("/", ".");
				Class c = Class.forName(classNamePackage);
				result.add(c);
				if (entryName.substring(entryName.lastIndexOf(File.separator) + 1).equals(className + ".class"))
					klass = c;
			}
		}
		return result;
	}

	public static void main(final String args[])
	{
	}
}
